/*!
 *  \file       ProbabilityDistribution.cpp
 *  \brief      Implementazione della classe \c ProbabilityDistribution
 *  \author     Davide Scola <S2299453@studenti.unige.it>
 *  \author	Minetti Alberto <S3064777@studenti.unige.it>
 *  \version    1.0
 */
#include <cmath>
#include <vector>
#include "IEntropyGenerator.hpp"
#include "IRandomGenerator.hpp"
#include "SimpleRandomGenerator.hpp"
#include "ProbabilityDistribution.hpp"

using namespace std;


/*!
 *  \brief      Costruttore di copia di \c ProbabilityDistribution
 *  \param[in]	other	distribuzione di probabilita` da copiare
 */
template< typename tType, template< typename > class tDistribution, typename rType >
ProbabilityDistribution< tType, tDistribution, rType >::ProbabilityDistribution( ProbabilityDistribution< tType, tDistribution, rType > const& other )
    : vector< tType >( static_cast< vector< tType > >( other ) ),
      random_( (&other)->random_ )
{
}

/*!
 *  \brief      Costruttore di \c ProbabilityDistribution
 *  \param[in]	population	numero di campioni da generare
 *  \param[in]	random		distribuzione di probabilita` da utilizzare
 */
template< typename tType, template< typename > class tDistribution, typename rType >
ProbabilityDistribution< tType, tDistribution, rType >::ProbabilityDistribution( const unsigned long int population, tDistribution< tType > random )
    : vector< tType >( population ),
      random_( random )
{
  for( unsigned long int i = 0; i < this->size(); ++i )
    (*this)[i] = this->random_.next();
}

/*!
 *  \brief      Operatore di assegnazione della classe \c ProbabilityDistribution
 */
template< typename tType, template< typename > class tDistribution, typename rType >
ProbabilityDistribution< tType, tDistribution, rType >&
ProbabilityDistribution< tType, tDistribution, rType >::operator=( const ProbabilityDistribution & other )
{
   if (this == &other) {
       return *this;
   }
   
   this->random_ = (&other)->random_;
   this->resize( (&other)->size( ) );
   
   for( unsigned long int i = 0; i < this->size(); ++i )
    (*this)[i] = other[i];
   
   return *this;
}


/*!
 *  \brief      Calcola la media aritmetica della popolazione statistica
 */
template< typename tType, template< typename > class tDistribution, typename rType > rType
ProbabilityDistribution< tType, tDistribution, rType >::mean( void )
{
  rType sum = rType( 0 );
  
  for( unsigned long int i = 0; i < this->size(); ++i )
    sum += (*this)[i];
  
  return sum / this->size();  
}

/*!
 *  \brief      Calcola la varianza della popolazione statistica
 */
template< typename tType, template< typename > class tDistribution, typename rType > rType
ProbabilityDistribution< tType, tDistribution, rType >::variance( void )
{
  rType _variance = rType( 0 );
  rType _mean = this->mean( );
  
  for( unsigned long int i = 0; i < this->size(); ++i )
    _variance += pow( (*this)[i] - _mean, 2 ) ;
  
  return _variance / ( this->size() - 1 ); 
}

/*!
 *  \brief      Calcola la deviazione standard della popolazione statistica
 */
template< typename tType, template< typename > class tDistribution, typename rType > rType
ProbabilityDistribution< tType, tDistribution, rType >::stDeviation( void )
{
  return rType( sqrt( this->variance( ) ) );
}
